package cn.com.twoke.application.game.mario.game.level.file;

import cn.com.twoke.application.game.mario.core.ecs.world.GameWorld;
import cn.com.twoke.application.game.mario.core.ecs.component.PositionComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.SizeComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.Vector2f;
import cn.com.twoke.application.game.mario.core.ecs.component.render.animation.AnimationRenderComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.render.group.GroupRenderComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.render.text.TextRenderComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.render.texture.TextureRenderComponent;
import cn.com.twoke.application.game.mario.core.ecs.entity.Entity;
import cn.com.twoke.application.game.mario.game.component.*;
import cn.com.twoke.application.game.mario.core.ecs.level.AbstractLevel;
import cn.com.twoke.application.game.mario.game.entity.BlockBuilder;
import cn.com.twoke.application.game.mario.game.entity.BuildType;
import cn.com.twoke.application.game.mario.game.entity.EntityType;
import cn.com.twoke.application.game.mario.game.entity.PipeBuilder;
import cn.com.twoke.application.game.mario.game.scene.CameraPayload;
import cn.com.twoke.application.game.mario.game.scene.CameraType;
import cn.com.twoke.application.game.mario.game.util.TextureUtils;
import cn.com.twoke.application.game.mario.game.config.Tiles;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;

import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * 通过文件加载
 */
public class JsonFileLevel extends AbstractLevel {

    static BufferedImage tileset1;
    static BufferedImage marioSprite;// 精灵图

    static {
        tileset1 = TextureUtils.loadImage("texture/tileset1.png");
        marioSprite = TextureUtils.loadImage("texture/mario.png");
    }

    private final String fileName;
    private final Map<BuildType, Consumer<JSONObject>> entitiesHandler = new HashMap<>();

    public JsonFileLevel(String fileName) {
        this.fileName = fileName;
        this.entitiesHandler.put(BuildType.PLAYER, this::initPlayer);
        this.entitiesHandler.put(BuildType.NORMAL_BLOCK, this::initNormalBlock);
        this.entitiesHandler.put(BuildType.BACKGROUND, this::initBackground);
        this.entitiesHandler.put(BuildType.PIPE, this::initPipe);
        this.entitiesHandler.put(BuildType.TRANSFER, this::initTransfer);
        this.entitiesHandler.put(BuildType.ENEMY, this::initEnemies);
    }// 重构

    private static Supplier<BufferedImage> getUndergroundBlock(int x, int y) {
        BufferedImage copyImage = TextureUtils.changeColor(tileset1.getSubimage(16 * x, y, 16, 16), new int[]{0x9C4A00, 0xFFCEC5}, new int[]{0x007B8B, 0xB5EFEF});
        return () -> copyImage;
    }

    @SneakyThrows
    @Override
    public void init(GameWorld world) {
        String levelData = new String(IoUtil.readBytes(getInputStream()));
        JSONObject entries = JSONUtil.parseObj(levelData);
        // 初始化场景信息
        initScenes(entries.getJSONArray("scenes"));
        // 初始化实体信息
        initEntities(entries.getJSONArray("entities"));

    }

    private void initEntities(JSONArray entities) {
        for (Object entity : entities) {
            JSONObject entityObj = (JSONObject) entity;
            BuildType type = BuildType.valueOf(entityObj.getStr("type"));
            if (entitiesHandler.containsKey(type)) {
                entitiesHandler.get(type).accept(entityObj.getJSONObject("options"));
            }
        }
    }

    /**
     * 初始化场景
     *
     * @param scenes
     */
    private void initScenes(JSONArray scenes) {
        for (Object scene : scenes) {
            JSONObject sceneObj = (JSONObject) scene;
            String name = sceneObj.getStr("name");// 场景名称
            String type = sceneObj.getStr("type");// 场景相机类型
            CameraPayload.CameraPayloadBuilder cameraPayloadBuilder = CameraPayload.builder()
                    .cameraType(CameraType.valueOf(type));
            if (sceneObj.containsKey("position")) {
                JSONObject position = sceneObj.getJSONObject("position");
                cameraPayloadBuilder.position(Vector2f.obtain(Tiles.widthOf(position.getInt("x")), Tiles.heightOf(position.getInt("y"))));
            }
            if (sceneObj.containsKey("size")) {
                JSONObject size = sceneObj.getJSONObject("size");
                cameraPayloadBuilder.size(Vector2f.obtain(Tiles.widthOf(size.getInt("width")), Tiles.heightOf(size.getInt("height"))));
            }
            if (sceneObj.containsKey("offset")) {
                JSONObject offset = sceneObj.getJSONObject("offset");
                cameraPayloadBuilder.offset(Vector2f.obtain(Tiles.widthOf(offset.getInt("x")), Tiles.heightOf(offset.getInt("y"))));
            }
            sceneManager.createScene(name, cameraPayloadBuilder.build());
        }
    }

    @Override
    public String getName() {
        return this.fileName;
    }

    private InputStream getInputStream() {
        return JsonFileLevel.class.getClassLoader().getResourceAsStream("levels/" + fileName + ".json");
    }

    /**
     * 初始化玩家
     *
     * @param entries
     */
    private void initPlayer(JSONObject entries) {
        Integer startX = entries.getInt("startX");
        Integer startY = entries.getInt("startY");
        String name = entries.getStr("initSceneName");
        Entity player = entityBuilderFactory.player()
                .startX(Tiles.widthOf(startX))
                .startY(Tiles.heightOf(startY))
                .width(Tiles.widthOf(1))
                .height(Tiles.heightOf(1))
                .initSceneName(name)
                .initMoveSpeed(2)
                .playerName("热爱苛抵万难").build();
        PlayerComponent component = player.getComponent(PlayerComponent.class);
        GroupRenderComponent groupRenderComponent = GroupRenderComponent.builder().build();
        player.addComponent(groupRenderComponent);
        // 移动动画
        groupRenderComponent.add(AnimationRenderComponent.builder()
                .isShow(() -> (component.isLeft || component.isRight) && !component.isJump)
                .animationTickCount(3)
                .animationTickTextureFun(idx -> {
                    if (component.getDirection() == PlayerComponent.Direction.LEFT) {
                        return TextureUtils.reverse(marioSprite.getSubimage(16 + 16 * idx, 0, 16, 16));
                    } else {
                        return marioSprite.getSubimage(16 + 16 * idx, 0, 16, 16);
                    }
                })
                .build());
        // 名字
        groupRenderComponent.add(TextRenderComponent.builder()
                .text(component.name)
                .offsetY(-5)
                .build());
        // 跳跃动画
        groupRenderComponent.add(AnimationRenderComponent.builder()
                .isShow(() -> component.isJump)
                .animationTickCount(1)
                .animationTickTextureFun(idx -> {
                    if (component.getDirection() == PlayerComponent.Direction.LEFT) {
                        return TextureUtils.reverse(marioSprite.getSubimage(16 * 5, 0, 16, 16));
                    } else {
                        return marioSprite.getSubimage(16 * 5, 0, 16, 16);
                    }
                })
                .build());
        // 默认状态
        groupRenderComponent.add(AnimationRenderComponent.builder()
                .isShow(() -> !component.isJump && !component.isLeft && !component.isRight)
                .animationTickCount(1)
                .animationTickTextureFun(idx -> {
                    if (component.getDirection() == PlayerComponent.Direction.LEFT) {
                        return TextureUtils.reverse(marioSprite.getSubimage(0, 0, 16, 16));
                    } else {
                        return marioSprite.getSubimage(0, 0, 16, 16);
                    }
                })
                .build());

    }

    /**
     * 初始化普通模块
     *
     * @param entries
     */
    private void initNormalBlock(JSONObject entries) {
        // 准备参数
        EntityType type = EntityType.valueOf(entries.getStr("type"));
        BlockBuilder blockBuilder = entityBuilderFactory.block()
                .type(type)
                .startX(Tiles.widthOf(entries.getInt("startX")))
                .startY(Tiles.heightOf(entries.getInt("startY")))
                .tileCols(entries.getInt("tileCols"))
                .tileRows(entries.getInt("tileRows"));
        Boolean underground = Opt.ofNullable(entries.getBool("underground")).orElse(false);
        if (type == EntityType.WALL && underground) {
            Supplier<BufferedImage> undergroundBlockWall = getUndergroundBlock(2, 0);
            blockBuilder.fillComponent(new BlockRenderFillComponent(undergroundBlockWall));
        }
        if (type == EntityType.GROUND && underground) {
            Supplier<BufferedImage> undergroundBlockGround = getUndergroundBlock(0, 0);
            blockBuilder.fillComponent(new BlockRenderFillComponent(undergroundBlockGround));
        }
        // 执行构建
        blockBuilder.build();
    }


    /**
     * 初始化背景
     *
     * @param entries
     */
    private void initBackground(JSONObject entries) {
        // 背景
        Entity entity = entityBuilderFactory.createEntity();
        Integer startX = entries.getInt("startX");
        Integer startY = entries.getInt("startY");
        Integer width = entries.getInt("width");
        Integer height = entries.getInt("height");
        String textureId = entries.getStr("textureId");
        entity.addComponent(new PositionComponent(startX, startY));
        entity.addComponent(new SizeComponent(Tiles.widthOf(width), Tiles.heightOf(height)));
        entity.addComponent(TextureRenderComponent.builder()
                .textureId(() -> textureId)
                .build());
    }

    /**
     * 初始化传送门
     *
     * @param entries
     */
    private void initTransfer(JSONObject entries) {
        entityBuilderFactory.transfer()
                .startX(Tiles.widthOf(entries.getInt("startX")))
                .startY(Tiles.widthOf(entries.getInt("startY")))
                .width(Tiles.scaleOf(entries.getInt("width")))
                .height(Tiles.scaleOf(entries.getInt("height")))
                .targetSceneName(entries.getStr("targetSceneName"))
                .type(TransfersComponent.TransferType.valueOf(entries.getStr("type")))
                .entryName(entries.getStr("name"))
                .build();
    }


    /**
     * 初始化敌人
     * @param entries
     */
    private void initEnemies(JSONObject entries) {
        entityBuilderFactory.enemy()
                .startX(Tiles.widthOf(entries.getInt("startX")))
                .startY(Tiles.heightOf(entries.getInt("startY")))
                .name(entries.getStr("name", ""))
                .autoMoving(entries.getBool("autoMoving", false))
                .build()
        ;
    }

    /**
     * 初始化管道
     *
     * @param entries
     */
    private void initPipe(JSONObject entries) {
        PipeBuilder pipeBuilder = entityBuilderFactory.pipe()
                .startX(Tiles.widthOf(entries.getInt("startX")))
                .startY(Tiles.heightOf(entries.getInt("startY")))
                .verticalLength(Opt.ofNullable(entries.getInt("verticalLength")).orElse(1))
                .horizontalLength(Opt.ofNullable(entries.getInt("horizontalLength")).orElse(1))
                .pipeType(PipeComponent.PipeType.valueOf(Opt.ofNullable(entries.getStr("type")).orElse(PipeComponent.PipeType.VERTICAL.name())))
                .isEmbeddedWall(Opt.ofNullable(entries.getBool("isEmbeddedWall")).orElse(false));

        if (entries.containsKey("transfers")) {
            JSONArray transfers = entries.getJSONArray("transfers");
            pipeBuilder.addTransferAfterHook(transfer -> {
                for (Object transferConfig : transfers) {
                    JSONObject transferObject = (JSONObject) transferConfig;
                    if (transfer.direction == TransfersComponent.Direction.valueOf(transferObject.getStr("dir"))) {
                        transfer.enabled = true;
                        transfer.type = TransfersComponent.TransferType.valueOf(transferObject.getStr("type"));
                        transfer.setName(transferObject.getStr("name"));
                        transfer.setTargetSceneName(transferObject.getStr("scene"));
                    }
                }
            });
        }

        if (Opt.ofNullable(entries.getBool("underground")).orElse(false)) {
            pipeBuilder.extend(pipeEntity -> {
                pipeEntity.addComponent(new TextureDealComponent(image -> TextureUtils.changeColor(image, new int[]{0x000000}, new int[]{0x084A00})));
            });
        }
        pipeBuilder.build();
    }
}
